<?php

namespace liketp\database;

use liketp\exception\Exception;
use PDO;

abstract class Connection
{
    protected $config;
    protected $pdo;
    protected static $defaultConfig = [];

    abstract public static function flag(array $config): string;
    abstract protected function createPdo();
    protected function initialize() {}

    public function __construct($config)
    {
        $this->config = $config;
        $this->pdo = $this->createPdo($config);
        $this->initialize();
    }

    public static function setConfig($config)
    {
        if (isset(static::$defaultConfig)) {
            $config = $config + static::$defaultConfig;
        }
        return $config;
    }


    //------------------------------------------------ SQL method
    public function select($sql, $params)
    {
        $stm = $this->pdo->prepare($sql);
        $this->bindData($stm, $params);
        $execResult = $stm->execute();

        $this->checkExecResult($execResult, $stm);

        return $stm->fetchAll(PDO::FETCH_ASSOC);
    }

    public function execute($sql, $params, string $return = 'execute_result')
    {
        $stm = $this->pdo->prepare($sql);
        $this->bindData($stm, $params);
        $result = $stm->execute();

        $this->checkExecResult($result, $stm);

        if ($return == 'execute_result') return $result;
        if ($return == 'pdo_statement') return $stm;
        if ($return == 'pdo') return $this->pdo;
        if ($return == 'row_count') return $stm->rowCount();
    }


    //------------------------------------------------ protected
    protected function bindData($stm, $data)
    {
        $bindType = is_numeric(array_key_first($data)) ? '?' : ':name';
        foreach ($data as $k => $v) {
            $key = $bindType == '?' ? $k + 1 : $k;
            [$val, $type] = (array)$v + [1 => PDO::PARAM_STR];
            $stm->bindValue($key, $val, $type);
        }
    }

    protected function checkExecResult($result, $stm)
    {
        if ($result == false) {
            $errorInfo = $stm->errorInfo();
            if (isset($errorInfo[0]) && $errorInfo[0] != '00000') {
                $msg = $errorInfo[2] ?? 'SQL execute failed';
                throw new Exception($msg);
            }
        }
    }
}
